package com.demo.util;

import com.google.i18n.phonenumbers.NumberParseException;
import com.google.i18n.phonenumbers.PhoneNumberUtil;
import com.google.i18n.phonenumbers.Phonenumber;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;


/**
 * 参数处理工具
 */
@Slf4j
public final class ParamCheckUtil {

  private static final Pattern EMAIL_FIRST_SEGMENT_PATTERN = Pattern.compile("[a-zA-Z0-9][a-zA-Z0-9._-]*");
  private static final Pattern EMAIL_SECOND_SEGMENT_PATTERN = Pattern.compile("[a-zA-Z0-9_-]+");
  private static final Pattern IPV4_PATTERN = Pattern.compile(
      "^(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)(\\.(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)){3}(/([1-9]|1\\d|2\\d|3[0-2]))?$");
  private static final Pattern IPV6_PATTERN = Pattern.compile(
      "((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|" +
          "((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3})|:))|" +
          "(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)" +
          "(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|" +
          "((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|" +
          "(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)" +
          "(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|" +
          "2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|" +
          "((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|" +
          "((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:)))(%.+)?"
  );

  private static final Pattern NUMBER_PATTERN = Pattern.compile("[0-9]*");


  private static final Pattern AUTH_CODE = Pattern.compile("^\\d{6}$");

  /**
   * 手机号正则
   */
  static Pattern PHONE_PATTERN = Pattern.compile("^[0-9]{5,15}$");

  /**
   * 手机区号正则
   */
  static Pattern COUNTRY_CDOE_PATTERN = Pattern.compile("^[0-9]{1,6}$");


  /**
   * 检查字符串内容是否为数字
   */
  public static boolean checkNumber(String str, int count) {
    if (StringUtils.isBlank(str)) {
      return false;
    }
    if (count > 0 && str.length() != count) {
      return false;
    }
    return NUMBER_PATTERN.matcher(str).matches();
  }

  /**
   * 检查邮箱是否正确
   * 1.是否为空
   * 2.长度是否匹配
   * 3.是否包含@。@前后的字符是否满足要求
   */
  public static boolean checkEmail(String email) {
    if (StringUtils.isBlank(email)) {
      return false;
    }
    if (!validLength(email, 6, 64)) {
      return false;
    }

    String[] array = email.split("@");
    if (array.length != 2 || StringUtils.isBlank(array[0]) || StringUtils.isBlank(array[1])) {
      return false;
    }
    boolean preMatch = EMAIL_FIRST_SEGMENT_PATTERN.matcher(array[0]).matches();
    if (!preMatch) {
      return false;
    }
    String[] subArray = array[1].split("\\.");
    if (subArray.length < 2) {
      return false;
    }
    for (String subStr : subArray) {
      if (StringUtils.isBlank(subStr)) {
        return false;
      }
      if (!EMAIL_SECOND_SEGMENT_PATTERN.matcher(subStr).matches()) {
        return false;
      }
    }
    return true;
  }

  /**
   * 校验字符串长度
   *
   * @param s
   * @param min
   * @param max
   * @return
   */
  public static boolean validLength(String s, int min, int max) {
    if (s == null) {
      return true;
    }
    if (s.length() >= min && s.length() <= max) {
      return true;
    }
    return false;
  }

  /**
   * 如果为空，返回true
   * 如果不为空，在6～64中，返回true.
   *
   * @param email
   * @return
   */
  public static boolean checkEmailLength(String email) {
    if (!validLength(email, 6, 64)) {
      return false;
    }
    return true;
  }

  /**
   * 检查 手机区号
   * 1.是否为空
   * 2.是否匹配0～9的数字
   * 3.是否在1-6位。
   * 4.是否能找到该区号
   *
   * @param countryCode 区号
   * @return
   */
  public static boolean checkCountryCode(String countryCode) {
    if (StringUtils.isBlank(countryCode)) {
      return false;
    }
    Matcher matcher = COUNTRY_CDOE_PATTERN.matcher(countryCode);
    if (!matcher.matches()) {
      return false;
    }
    if (Country.findByCode(countryCode) == null) {
      return false;
    }
    return true;
  }

  /**
   * 如果为空，返回true
   * 如果不为空，在1～6中，返回true.
   *
   * @param countryCode 区号
   * @return
   */
  public static boolean checkCountryCodeLength(String countryCode) {
    if (!validLength(countryCode, 1, 6)) {
      return false;
    }
    return true;
  }

  /**
   * 检查手机号是否正确.
   * 1.手机号码是否为空。
   * 2.手机号码是否匹配0-9的数字，是否在 5到15位之内。
   */
  public static boolean checkPhone(String mobileNumber) {
    if (StringUtils.isBlank(mobileNumber)) {
      return false;
    }
    Matcher matcher = PHONE_PATTERN.matcher(mobileNumber);
    return matcher.matches();
  }

  public static boolean checkPhoneLength(String mobileNumber) {
    return validLength(mobileNumber, 5, 15);
  }

  public static boolean checkAuthCodeLength(String authCode) {
    return validLength(authCode, 6, 6);
  }

  /**
   * 根据电话号码和国家代码校验该手机号是否符合该国的手机号规则,符合则返回过滤号的真实手机号，不符合返回空
   * 如果前端传过来的手机号前面带零（比如 018810751234），ParamHandleUtil.checkPhone 方法会把前面的 "0" 去掉并返回新字符串 18810751234
   */
  public static String checkPhone(String phone, String countryCode) {
    if (StringUtils.isEmpty(phone) || StringUtils.isEmpty(countryCode)) {
      return null;
    }
    if (countryCode.equals(Country.JAMAICA.getCode())) {
      return phone;
    }
    PhoneNumberUtil phoneUtil = PhoneNumberUtil.getInstance();
    try {
      String regionCode = null;
      for (Country c : Country.values()) {
        if (countryCode.equals(c.getCode())) {
          regionCode = c.getKeyword();
          break;
        }
      }
      if (regionCode == null) {
        log.error("地区码为空，phone = {}，countryCode = {}", phone, countryCode);
        return null;
      }

      Phonenumber.PhoneNumber numberProto = phoneUtil.parse(phone, regionCode);
      if (!phoneUtil.isValidNumber(numberProto)) {
        return null;
      }
      //获取除掉countryCode的真实的手机号
      return String.valueOf(numberProto.getNationalNumber());
    } catch (NumberParseException e) {
      log.error("解析异常，phone = {}，countryCode = {}", phone, countryCode);
    } catch (NoSuchElementException e) {
      log.error("地区码不存在，phone = {}，countryCode = {}", phone, countryCode);
    }
    return null;
  }

  private static Set<String> regionCodes = new HashSet<>();

  static {
    regionCodes.addAll(Arrays.asList(
        "CN", //中国
        "KR", // 韩国
        "JP", // 日本
        "AU", // 澳大利亚
        "US", // 美国
        "ID", // 印度
        "DE", // 德国
        "RU", // 俄罗斯
        "HK", // 香港
        "TW", // 台湾
        "GB" // 英国
    ));
  }

  /**
   * 根据电话号码和国家代码校验该手机号是否符合该国的手机号规则,符合返回过滤号的真实手机号，不符合返回空
   */
  public static String getValidPhone(String phone) {
    if (StringUtils.isEmpty(phone)) {
      return null;
    }

    PhoneNumberUtil phoneUtil = PhoneNumberUtil.getInstance();
    String validPhone;
    // 先利用常用地区缓存，缓存中有，减少遍历次数；没有，需要遍历
    for (String regionCode : regionCodes) {
      try {
        validPhone = getValidNumber(phoneUtil, phone, regionCode);
        if (validPhone != null) {
          return validPhone;
        }
      } catch (NumberParseException e) {
        if (e.getErrorType() != NumberParseException.ErrorType.INVALID_COUNTRY_CODE) {
          return null;
        }
      }
    }

    String regionCode;
    for (Country c : Country.values()) {
      try {
        regionCode = c.getKeyword();
        if (regionCodes.contains(regionCode)) {
          continue;
        }
        validPhone = getValidNumber(phoneUtil, phone, regionCode);
        if (validPhone != null) {
          return validPhone;
        }
      } catch (NumberParseException e) {
        if (e.getErrorType() != NumberParseException.ErrorType.INVALID_COUNTRY_CODE) {
          return null;
        }
      }
    }
    return null;
  }

  private static String getValidNumber(PhoneNumberUtil phoneUtil, String phone, String regionCode) throws NumberParseException {
    Phonenumber.PhoneNumber numberProto = phoneUtil.parse(phone, regionCode);
    if (phoneUtil.isValidNumber(numberProto)) {
      //获取除掉countryCode的真实的手机号
      return String.valueOf(numberProto.getNationalNumber());
    }
    return null;
  }

//  public static boolean checkIPAddress(String ip) {
//    Matcher matcherIpv6 = IPV6_PATTERN.matcher(ip);
//    // ipv6不判断是否内网ip
//    if (matcherIpv6.matches()) {
//      return true;
//    }
//    Matcher matcher = IPV4_PATTERN.matcher(ip);
//    if (matcher.matches() && checkoutIpNetmask(ip)) {
//      byte[] addr = IPAddressUtil.textToNumericFormatV4(ip);
//      // 如果是内网ip返回false
//      return !internalIp(addr);
//    } else {
//      return false;
//    }
//  }

  public static boolean internalIp(byte[] addr) {
    final byte addrSection1 = addr[0];
    final byte addrSection2 = addr[1];
    //10.x.x.x/8
    final byte section1 = 0x0A;
    //172.16.x.x/12
    final byte section2 = (byte) 0xAC;
    final byte section3 = (byte) 0x10;
    final byte section4 = (byte) 0x1F;
    //192.168.x.x/16
    final byte section5 = (byte) 0xC0;
    final byte section6 = (byte) 0xA8;
    //0.x.x.x/8
    final byte section7 = (byte) 0x00;
    //127.x.x.x/8
    final byte section8 = (byte) 0x7F;

    switch (addrSection1) {
      case section1:
      case section7:
      case section8:
        return true;
      case section2:
        if (addrSection2 >= section3 && addrSection2 <= section4) {
          return true;
        }
      case section5:
        if (addrSection2 == section6) {
          return true;
        }
      default:
        return false;
    }
  }

  /**
   * 检查ip是否正确
   */
  public static boolean checkIP(String ip) {
    if (StringUtils.isBlank(ip)) {
      return false;
    }
    Matcher matcher = IPV4_PATTERN.matcher(ip);
    if (matcher.matches() && checkoutIpNetmask(ip)) {
      return true;
    }
    Matcher matcher_ipv6 = IPV6_PATTERN.matcher(ip);
    return matcher_ipv6.matches();
  }

  private static boolean checkoutIpNetmask(String ip) {
    int index = ip.lastIndexOf('/');
    if (index == -1) {
      return true;
    }

    String realIp = ip.substring(0, index);

    int netNum = Integer.parseInt(ip.substring(index + 1));
    String[] ips = realIp.split("\\.");
    int temp = 0;
    int i;
    for (i = 3; i >= 0; i--) {
      temp = Integer.parseInt(ips[i]);
      if (temp != 0) {
        break;
      }
    }

    int j;
    for (j = 7; j >= 0; j--) {
      int current = 1 << j;
      if (temp - current == 0) {
        break;
      }
      if (temp - current > 0) {
        temp -= current;
      }
    }

    int current = i * 8 + 8 - j;

    return netNum >= current && netNum <= 32;
  }

  /**
   * 检查是否为IPv4
   */
  public static boolean checkIPv4(String ip) {
    if (StringUtils.isBlank(ip)) {
      return false;
    }
    Matcher matcher = IPV4_PATTERN.matcher(ip);
    return matcher.matches();
  }

  /**
   * 验证码是否正确
   */
  public static boolean checkAuthCode(String authCode) {
    if (StringUtils.isBlank(authCode)) {
      return false;
    }
    Matcher matcher = AUTH_CODE.matcher(authCode);
    return matcher.matches();
  }

  /**
   * 检查传入内容是数字、字母集
   */
  public static boolean charAndNumberVerify(String data, int length) {
    return data.length() == length && data.matches("[a-z0-9A-Z]+");
  }


  /**
   * 分隔字符串
   *
   * @param value
   * @param delim
   * @return
   */
  public static List<String> split(String value, char delim) {
    final int end = value.length();
    final List<String> res = new ArrayList<>();

    int start = 0;
    for (int i = 0; i < end; i++) {
      if (value.charAt(i) == delim) {
        if (start == i) {
          res.add("");
        } else {
          res.add(value.substring(start, i));
        }
        start = i + 1;
      }
    }
    if (start == 0) {
      res.add(value);
    } else if (start != end) {
      res.add(value.substring(start, end));
    } else {
      for (int i = res.size() - 1; i >= 0; i--) {
        if (res.get(i).isEmpty()) {
          res.remove(i);
        } else {
          break;
        }
      }
    }

    return res;
  }

}
